package ua.com.minus1.lookbattle.net;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.widget.ImageView;
import ua.com.fedorvlasov.lazylist.MemoryCache;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created with IntelliJ IDEA.
 * User: Flash
 * Date: 11.07.12
 * Time: 12:19
 * To change this template use File | Settings | File Templates.
 */
public class ImageManager {

    public static final String USE_CACHE = "useCache";

    private final String TAG = "ImageManager";

    private Map<ImageView, String> imageTagMap;
    private ExecutorService executorService;
    private HttpFileCache fileCache;
    private MemoryCache memoryCache;
    private int defaultResourceId = -1;

    public ImageManager(Context context) {
        init(context, "");
    }

    public ImageManager(Context context, String folder) {
        init(context, folder);
    }

    private void init(Context context, String folder) {
        fileCache = new HttpFileCache(context, folder);
        memoryCache = new MemoryCache();
        executorService = Executors.newFixedThreadPool(5);
        imageTagMap = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
    }

    public ImageManager setDefaultResourceId(int id) {
        defaultResourceId = id;
        return this;
    }

    public void loadBitmap(String url, ImageView imageView) {
        startBitmapLoading(imageView.getContext(), url, imageView, null, null);
    }

    public void loadBitmap(String url, ImageView imageView, BitmapLoadListener bitmapLoadListener) {
        startBitmapLoading(imageView.getContext(), url, imageView, bitmapLoadListener, null);
    }

    public void loadBitmap(String url, ImageView imageView, BitmapLoadListener bitmapLoadListener, String param) {
        startBitmapLoading(imageView.getContext(), url, imageView, bitmapLoadListener, param);
    }

    public void loadBitmap(String url, ImageView imageView, String param) {
        startBitmapLoading(imageView.getContext(), url, imageView, null, param);
    }

    public void loadBitmap(Context context, String url, BitmapLoadListener bitmapLoadListener) {
        startBitmapLoading(context, url, null, bitmapLoadListener, null);
    }

    public void loadBitmap(Context context, String url, BitmapLoadListener bitmapLoadListener, String param) {
        startBitmapLoading(context, url, null, bitmapLoadListener, param);
    }

    public void stopLoading(ImageView imageView) {
        if(imageTagMap.containsKey(imageView))
            imageTagMap.remove(imageView);
    }

    private void startBitmapLoading(Context context, String url, ImageView imageView, BitmapLoadListener bitmapLoadListener, String param) {
        boolean useCache = param != null && param.equals(USE_CACHE);

        if (imageView != null)
            imageTagMap.put(imageView, url);

        if (useCache) {
            Bitmap bitmap = memoryCache.get(url);
            if (bitmap != null) {
                if (imageView != null)
                    imageView.setImageBitmap(bitmap);

                if (bitmapLoadListener != null)
                    bitmapLoadListener.onLoadComplete(bitmap);

                return;
            }
        }

        PhotoToLoad photoToLoad = new PhotoToLoad(context, url, imageView, bitmapLoadListener, useCache);
        executorService.submit(new PhotoLoader(photoToLoad));

        if (defaultResourceId != -1 && imageView != null)
            imageView.setImageResource(defaultResourceId);
    }

    //retrieve bitmap from url and write it to cache if needed
    private Bitmap getBitmap(String url, Boolean useCache)
    {
        File f=null;
        Bitmap bitmap;
        //from SD cache
        if (useCache) {
            f = fileCache.getFile(url);
            bitmap = decodeInputStream(new InputStreamFactory(f));
            if(bitmap != null) {
                Log.i(TAG, "bitmap from file cache");
                memoryCache.put(url, bitmap);
                return bitmap;
            }
        }
        //from web
        try {
            InputStream is=getInputStream(url);
            if (useCache) {
                OutputStream os = new FileOutputStream(f);
                Utils.copyStream(is, os);
                os.close();
                bitmap = decodeInputStream(new InputStreamFactory(f));
                memoryCache.put(url, bitmap);
            } else {
                ByteArrayOutputStream byteBuff = new ByteArrayOutputStream();
                Utils.copyStream(is, byteBuff);
                bitmap = decodeInputStream(new InputStreamFactory(byteBuff.toByteArray()));
            }
            return bitmap;
        } catch (IOException ex){
            ex.printStackTrace();
            return null;
        }
    }

    private Bitmap decodeInputStream(InputStreamFactory isFactory) {
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(isFactory.getInputStream(), null, o);

        int scale = calculateInSampleSize(o, 400, 400);
        //decode with inSampleSize
        o = new BitmapFactory.Options();
        o.inSampleSize = scale;
        return BitmapFactory.decodeStream(isFactory.getInputStream(), null, o);
    }

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;

        Boolean scaleByHeight = Math.abs(height - reqHeight) >= Math.abs(width - reqWidth);
        double sampleSize = scaleByHeight? height / reqHeight: width / reqWidth;
        return  (int)Math.pow(2d, Math.floor(Math.log(sampleSize)/Math.log(2d)));
    }

    private InputStream getInputStream(String u) throws IOException {
        HttpURLConnection conn = (HttpURLConnection) new URL(u).openConnection();
        conn.setConnectTimeout(30000);
        conn.setReadTimeout(30000);
        conn.setInstanceFollowRedirects(true);
        return conn.getInputStream();
    }

    public void clearCache() {
        memoryCache.clear();
        fileCache.clear();
    }

    boolean imageViewReused(PhotoToLoad photoToLoad){
        if(photoToLoad.imageView != null) {
            String tag = imageTagMap.get(photoToLoad.imageView);
            if (tag == null || !tag.equals(photoToLoad.url))
                return true;
        }
        return false;
    }

    class PhotoLoader implements Runnable {
        PhotoToLoad photoToLoad;
        PhotoLoader(PhotoToLoad photoToLoad){
            this.photoToLoad=photoToLoad;
        }

        public void run() {
            if(imageViewReused(photoToLoad))
                return;

            final Bitmap bitmap = getBitmap(photoToLoad.url, photoToLoad.useCache);

            if(imageViewReused(photoToLoad))
                return;

            Activity a = (Activity) photoToLoad.context;
            a.runOnUiThread(new Runnable() {
                public void run() {
                    if (!imageViewReused(photoToLoad)) {
                        if (photoToLoad.imageView != null)
                            photoToLoad.imageView.setImageBitmap(bitmap);

                        if(photoToLoad.bitmapLoadListener != null)
                            photoToLoad.bitmapLoadListener.onLoadComplete(bitmap);
                    }
                }
            });
        }
    }

    private class PhotoToLoad
    {
        public String url;
        public boolean useCache;
        public ImageView imageView;
        public BitmapLoadListener bitmapLoadListener;
        private Context context;

        public PhotoToLoad(Context c, String u, ImageView i, BitmapLoadListener b, boolean useCache){
            this.context = c;
            this.url = u;
            this.imageView = i;
            this.bitmapLoadListener = b;
            this.useCache = useCache;
        }
    }

    public interface BitmapLoadListener {
        void onLoadComplete(Bitmap bitmap);
    }

    private class InputStreamFactory{
        private File file;
        private byte[] buff;

        private InputStreamFactory(File f) {
            file = f;
        }

        private InputStreamFactory(byte[] bytes) {
            buff = bytes;
        }

        public InputStream getInputStream() {
            try {
                if (file != null)
                    return new FileInputStream(file);
                if (buff != null)
                    return new ByteArrayInputStream(buff);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

}
